package com.zhuhao.basic.optional;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.extra.expression.ExpressionException;
import com.zhuhao.webcrawler.User;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true，调用get()方法会返回该对象。Optional 是个容器：
 * 它可以保存类型T的值，或者仅仅保存null。Optional提供很多有用的方法，这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。
 * <p>
 * <p>
 * Optional 是java非常有用的一个补充，它旨在减少代码中的NullPointerExceptions，虽然不能百分之百的消除，但也是精心设计的。
 * 使用Optional 能更好的帮助我们创建可读性强，bug更少的应用程序。
 *
 * @Author zhuhao
 * @Date 2021/11/11 16:48
 * @desc
 */
public class MyOptional {

    /**
     * of
     * 为非null的值创建一个Optional。of方法通过工厂方法创建Optional类。需要注意的是，创建对象时传入的参数不能为null。如果传入参数为null，
     * 则抛出NullPointerException 。
     */
    @Test
    public void test1() {
        Optional<String> optional = Optional.of("xiaoming");
        System.out.println(optional.get());

        Optional<String> optional2 = Optional.of(null);
        System.out.println(optional2.get());
    }

    /**
     * ofNullable
     * 为指定的值创建一个Optional，如果指定的值为null，则返回一个空的Optional。
     */
    @Test
    public void test2() {
        Optional<String> optional = Optional.ofNullable("hello");
        System.out.println(optional.get());

        Optional<String> optional2 = Optional.ofNullable(null);
        System.out.println(optional2.get());
    }

    /**
     * isPresent
     * 值存在返回true，否则返回false
     */
    @Test
    public void test3() {
        Optional<Object> optional = Optional.ofNullable(null);
        System.out.println(optional.isPresent());

        Optional<Object> optional2 = Optional.ofNullable("null");
        System.out.println(optional2.isPresent());
    }

    /**
     * get
     * Optional有值就返回，没有抛出NoSuchElementException
     */
    @Test
    public void test4() {
        Optional<Object> optional2 = Optional.ofNullable("null222");
        System.out.println(optional2.get());

        Optional<Object> optional = Optional.ofNullable(null);
        System.out.println(optional.get());
    }

    /**
     * ifPresent
     * 如果Optional有值则调用consumer处理，否则不处理
     */
    @Test
    public void test5() {
        Optional<Object> optional2 = Optional.ofNullable("null222");
        optional2.ifPresent(x -> System.out.println(x));

        Optional<Object> optional = Optional.ofNullable(null);
        optional.ifPresent(x -> System.out.println(x));
    }

    /**
     * orElse
     * 如果有值则将其返回，否则返回指定的其它值
     */
    @Test
    public void test6() {
        Optional<Object> optional2 = Optional.ofNullable("null222");
        System.out.println(optional2.orElse("111"));

        Optional<Object> optional = Optional.ofNullable(null);
        System.out.println(optional.orElse("111"));
    }

    /**
     * orElseGet
     * orElseGet与orElse方法类似，区别在于得到的默认值。orElse方法将传入的字符串作为默认值，
     * orElseGet方法可以接受Supplier接口的实现用来生成默认值
     */
    @Test
    public void test7() {
        Optional<Object> optional2 = Optional.ofNullable("null222");
        System.out.println(optional2.orElseGet(() -> "111111"));

        Optional<Object> optional = Optional.ofNullable(null);
        System.out.println(optional.orElseGet(() -> "111111"));
    }

    /**
     * 当Optional 为空时，orElse和orElseGet 区别不大，
     * 但当Optional有值时，orElse仍然会去调用方法创建对象，而orElseGet不会再调用方法；
     * 在我们处理的业务数据量大的时候，这两者的性能就有很大的差异。
     */
    @Test
    public void test8() {
        Shop shop = null;
        Optional.ofNullable(shop).orElse(createNew());
        System.out.println("----");
        Optional.ofNullable(shop).orElseGet(() -> createNew());
        System.out.println();

        //当Optional有值时，orElse仍然会去调用方法创建对象，而orElseGet不会再调用方法；
        Shop shop1 = new Shop().setName("刚播i").setPrice(33);
        Optional.ofNullable(shop1).orElse(createNew());
        System.out.println("-------------");
        Optional.ofNullable(shop1).orElseGet(MyOptional::createNew);

    }

    public static Shop createNew() {
        System.out.println("create new shop");
        return new Shop("博士帽", 55);
    }

    /**
     * orElseThrow
     * 如果有值则将其返回，否则抛出supplier接口创建的异常。
     */
    @Test
    public void test9() {
        Optional<Object> optinal = Optional.ofNullable(null);
        try {
            optinal.orElseThrow(() -> new Exception("yi chang"));
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("info: " + e.getMessage());
        }

        System.out.println();

//        try {
//            optinal.orElseThrow();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

    /**
     * map
     * 如果有值，则对其执行调用mapping函数得到返回值
     * 如果返回值不为null，则创建包含mapping返回值的Optional作为map方法返回值，否则返回空Optional。
     * <p>
     * 此方法可用来判断对象的属性是否为空，不为空则取值，为空可以赋新值
     */
    @Test
    public void test10() {
        Shop shop = null;

        Optional<Shop> optional = Optional.ofNullable(shop);
        System.out.println(optional.map(Shop::toString).orElse("honghong"));

        System.out.println();
        shop = new Shop("bi bi", 34);
        Optional<Shop> optional2 = Optional.ofNullable(shop);
        System.out.println(optional2.map(Shop::getPrice).orElse(12D));
    }

    /**
     * flatMap
     * 果有值，为其执行mapping函数返回Optional类型返回值，否则返回空Optional
     * 与map不同的是，flatMap 的返回值必须是Optional，而map的返回值可以是任意的类型T
     */
    @Test
    public void test11() {
        Shop shop = new Shop("1", 2);

        Optional<Shop> optional = Optional.ofNullable(shop);
        //参数的返回值必须是optional
        optional.flatMap(e -> Optional.of("eee")).orElse("222");

//        optional.flatMap(Shop::getPrice).orElse(12D);
        System.out.println(optional);
    }

    /**
     * filter
     */
    @Test
    public void test12() {
        List<String> list = Arrays.asList("out", "dfv", "baba", "other");
        for (String str : list) {
            String result = Optional.ofNullable(str).filter(x -> x.contains("o")).orElse("不包含o");
            System.out.println(result);
        }
    }

}
